#include "log.h"
#include <unistd.h>
#include <stdarg.h>
#include <time.h>
#include <thread>

Log* Log::mLog = nullptr;
std::string Log::mLogBuffer = "";
std::mutex Log::mMutexLog;
int Log::mWrittenSize = 0;
int Log::mFileFd = -1;
int gSemId = -1;

static int printfToBuffer(char* buffer, int size, char* format, ...){
	va_list ap;
	va_start(ap, format);
	int ret = vsnprintf(buffer, size, format, ap);
	va_end(ap);
	return ret;
}

static int getSystemTime(char* timeBuffer)
{
    if(!timeBuffer)
        return -1;

    struct timeval tv;
    gettimeofday(&tv, NULL);

    char format[] = "[%10d.%6d]";
    int ret = printfToBuffer(timeBuffer, 100, format, 
        tv.tv_sec,tv.tv_usec);
    
    return ret;
}

Log* Log::getInstance()
{
    if( mLog == nullptr)
    {
        std::lock_guard<std::mutex> lock(mMutexLog);
        if(nullptr == mLog)
        {
            printf("4444\n");
            mLog = new Log();
        }
    }
}

Log::Log()
{
    init(LOG_LEVEL_NONE, LOG_TARGET_FILE);
}

void Log::init(LOGLEVEL logLevel, LOGTARGET logTarget)
{
    setLogLevel(logLevel);
    setLogTarget(logTarget);
    if(createFile()<0)
        return;
    key_t key = ftok("/home/zhihui/test", 0666);
    gSemId = semget(key, 1, IPC_CREAT |0666);
    printf("key = 0x%x, gSemId = %d\n",key, gSemId );
    if(gSemId == -1){
        perror("semget");
        exit(EXIT_FAILURE);
    }

    union semun su;
    su.val = 1;
    int ret = semctl(gSemId, 0, SETVAL, su);
    if(ret == -1){
        perror("semctl");
        exit(EXIT_FAILURE);
    }
    return;

}
void Log::uinit()
{
    if(gSemId != -1){
        int ret;
        ret = semctl(gSemId, 0 ,IPC_RMID,0);
        if(ret == -1)
            perror("semctl");
    }
    if(mFileFd != -1){
        close(mFileFd);
    }
    return;

}

void Log::setLogLevel(LOGLEVEL level)
{
    this->mLogLevel = level;
    return;
}

LOGLEVEL Log::getLogLevel()
{
    return this->mLogLevel;
}

void Log::setLogTarget(LOGTARGET target)
{
    this->mLogTarget = target;
}

LOGTARGET Log::getLogTarget()
{
    return this->mLogTarget;
}

int Log::createFile()
{
    char* fileDir;
    //get currenDir
    if((fileDir=getcwd(NULL, 0)) == NULL)
    {
        perror("get cur dir!\n");
        return -1;
    }
    char logFileDir[MAX_PATH];
    sprintf(logFileDir, "%s/Log/", fileDir);
    printf("logFileDir:%s\n", logFileDir);
    if (access(logFileDir, 0) == -1)	//如果文件夹不存在
		if(mkdir(logFileDir, 0777) != 0)//则创建
            return -1;	

    time_t tt;
    time( &tt );
    tt = tt + 8*3600;  // transform the time zone
    tm* t= gmtime( &tt );

    char logFileName[MAX_PATH];
    sprintf(logFileName, "%s%d_%d_%d_%d_%d.log", 
            logFileDir,t->tm_year+1900,t->tm_mon+1, t->tm_mday, t->tm_hour,t->tm_min);
    printf("logFileName: %s\n", logFileName);
    mFileFd = open(logFileName, O_CREAT | O_RDWR ,0777);
    if(mFileFd == -1){
        perror("open fail!\n");
        return -1;
    }

    return 0;			
    
}

int Log::writeLog(
    LOGLEVEL loglevel,         // Log等级
    const char* fileName,   // 函数所在的文件名
    const char* function,   // 函数名
    int lineNumber,            // 行号
    char* format,              // 格式
    ...)
{

    //todo 锁
    struct sembuf sb = {0, -1, 0};
    int ret = semop(gSemId, &sb, 1);
    if(ret == -1){
        perror("semop");
        exit(EXIT_FAILURE);
    }

    char timeBuffer[100];
    ret = getSystemTime(timeBuffer);
    mLogBuffer += std::string(timeBuffer);

    std::string level;
    if(loglevel == LOG_LEVEL_DEBUG){
        level = "DEBUG";
    } else if(loglevel == LOG_LEVEL_INFO){
        level = "INFO";
    }else if(loglevel == LOG_LEVEL_WARNING){
        level = "WARNING";
    }else if(loglevel == LOG_LEVEL_INFO){
        level = "ERROR";
    }
    
    //[进程号][线程号][Log级别][文件名][函数名:行号]
    char locInfo[100];
    char format2[] = "[PID:%d][TID:%d][%s][%-s][%s:%d]";
    ret = printfToBuffer(locInfo, 100, format2,
        getppid(),
        std::this_thread::get_id(),
        level.c_str(),
        fileName,
        function,
        lineNumber);
    
    mLogBuffer += std::string(locInfo);
    

    //日志正文
    char logInfo2[256];
	va_list ap;
	va_start(ap, format);
	ret = vsnprintf(logInfo2, 256, format, ap);
	va_end(ap);

	mLogBuffer += std::string(logInfo2);
	mLogBuffer += std::string("\n");
	mWrittenSize += mLogBuffer.length();
    //todo
    outputToTarget();
    struct sembuf sbf = {0, 1, 0};
    ret = semop(gSemId, &sbf, 1);
    if(ret == -1){
        perror("semop");
        exit(EXIT_FAILURE);
    }
    return 0;
}

void Log::outputToTarget()
{
    if(Log::getInstance()->getLogTarget() & LOG_TARGET_FILE){
        lseek(mFileFd, 0, SEEK_END);
        int ret = write(mFileFd, mLogBuffer.c_str(), mLogBuffer.length());
        if(ret < 0){
            return;
        }
        fflush(NULL);
    }
    if(Log::getInstance()->getLogTarget() & LOG_TARGET_CONSOLE){
        printf("%s", mLogBuffer.c_str());
        fflush(stdout);
    }
    mLogBuffer.clear();
}
